public class RProp extends AbstractTrainingAlgorithm
2. Datenstrukturen für alle Daten, die der Algorithmus benötigt,
müssen deklariert werden. Hierzu gehören z.B. die Gradientenwerte
oder die Gewichtsveränderung aus dem letzten Lernschritt. Je nachdem,
ob es sich um einen einzelnen Wert für das gesamte Netz (z.B. Lernrate alpha)
oder um individuelle Werte pro Gewicht (z.B. Gradient des letzten Lernschrittes)
handelt, muss ein einzelner Wert oder ein Array deklariert werden.// Werte, die der Algorithmus benötigt // (ausser den Gewichten und den aktuellen Gradienten) // Initialisierung hier noch nicht möglich, // da das Netz noch unbekannt ist private double [][] mGradientWOld; private double [][] mDeltaIJ; private double [][] mWeightChange; private double mDeltaPlus; private double mDeltaMinus; private double mDeltaMax; private double mDeltaMin; private double mDeltaInit;3. Für jeden von außen konfigurierbaren Parameter wird eine Konstante mit dem Namen des Parameters benötigt. Es empfiehlt sich, die Texte in einer externen Properties-Datei auszulagern.
private static final ResourceBundle smMSCResourceBundle = ResourceBundle.getBundle("mscJNeuronalNet_Text"); // Von aussen konfigurierbare Parameter private static final String smParameterNames [] = { smMSCResourceBundle.getString("DELTA_PLUS"), smMSCResourceBundle.getString("DELTA_MINUS"), smMSCResourceBundle.getString("DELTA_MAX"), smMSCResourceBundle.getString("DELTA_MIN"), smMSCResourceBundle.getString("DELTA_ZERO") };4. Die Methode public String[] getLearningParameterNames() zum Abrufen dieser Parameternamen ist zu implementieren.
// Lesen der Bezeichnungen für die Parameter. // Reihenfolge wie bei setLearningParameters public String[] getLearningParameterNames() { return smParameterNames; }5. Die Methode public double[] getLearningParameters() zum Abrufen der Parameterwerte ist zu implementieren.
// Lesen der Parameter // Reihenfolge wie bei setLearningParameters public double[] getLearningParameters() { double [] lParameters = new double[smParameterNames.length]; lParameters[0] = mDeltaPlus; lParameters[1] = mDeltaMinus; lParameters[2] = mDeltaMax; lParameters[3] = mDeltaMin; lParameters[4] = mDeltaInit; return lParameters; }6. Die Methode public void setLearningParameters(double[] pLearningParameters) zum Festlegen der Parameterwerte von außen ist zu implementieren. Es sollte geprüft werden, ob ein Wert im gültigen Bereich (z.B. >= 0) ist, bevor er übernommen wird. Nach dem Verändern der Parameterwerte sind alle Observer des Lernverfahrens durch den Aufruf von getObserverManager().notifyObservers(this); zu benachrichtigen.
// Festlegen der Parameter von aussen // Die Reihenfolge der Parameter legt der Programmierer fest. Sie muss aber mit den // Methoden getLearningParameters, getLearningParameterNames und // getDefaultLearningParameters identisch sein. public void setLearningParameters(double[] pLearningParameters) { if (pLearningParameters.length>0){ if (pLearningParameters[0] >= 0D) mDeltaPlus = pLearningParameters[0]; if (pLearningParameters.length>1){ if (pLearningParameters[1] >= 0D) mDeltaMinus = pLearningParameters[1]; if (pLearningParameters.length>2){ if (pLearningParameters[2] >= 0D) mDeltaMax = pLearningParameters[2]; if (pLearningParameters.length>3){ if (pLearningParameters[3] >= 0D) mDeltaMin = pLearningParameters[3]; if (pLearningParameters.length>4){ if (pLearningParameters[4] >= 0D) mDeltaInit = pLearningParameters[4]; } } } } } getObserverManager().notifyObservers(this); // nicht vergessen ! }7. Defaultwerte für alle von außen veränderbaren Parameter sind festzulegen. Einfach ist es, die Defaultparameter in einem Array in derselben Reihenfolge wie in der Methode getLearningParameterNames() zu speichern. Dies erleichtert vor allem Punkt 8.
// Defaultwerte für die Parameter private static final double [] smParameterDefaultValues = {1.2, .5, 50D, .0000001, .1}; private double mDeltaPlus = smParameterDefaultValues[0]; private double mDeltaMinus = smParameterDefaultValues[1]; private double mDeltaMax = smParameterDefaultValues[2]; private double mDeltaMin = smParameterDefaultValues[3]; private double mDeltaInit = smParameterDefaultValues[4];8. Die Methode public double [] getDefaultLearningParameters() zum Abrufen der Defaultparameterwerte ist zu implementieren.
// Lesen der Default Parameterwerte. // Reihenfolge wie bei setLearningParameters public double [] getDefaultLearningParameters(){ return smParameterDefaultValues; }9. Die Methode public void init() zum Initialisieren der benötigten Speichervariablen ist zu implementieren. Vor dem Aufruf der Methode init() wurde dem Lernverfahren kein Netz zugeteilt, weswegen noch nicht feststeht, wie viele Gewichte vorhanden sind. Beim Aufruf der Methode init() ist das Netz bereits bekannt. Daher sind alle Initialisierungen hier durchzuführen. Der Zugriff auf das Netz geschieht mit der Methode getNet() aus AbstractTrainingAlgorithm.
// Initialisieren des benötigten Speichers. // Erst zu diesem Zeitpunkt ist das Netz gegeben public void init(){ super.init(); // nicht vergessen ! // Grösse der Speicher festlegen mGradientWOld = new double [getNet().getAmountNeurons()][getNet().getAmountNeurons()]; mDeltaIJ = new double [getNet().getAmountNeurons()][getNet().getAmountNeurons()]; mWeightChange = new double [getNet().getAmountNeurons()][getNet().getAmountNeurons()]; // Speicher mit Initialwerten füllen for (int x=0; x<getNet().getAmountNeurons(); x++){ for (int y=0; y<getNet().getAmountNeurons(); y++){ mGradientWOld[x][y] = 0D; mDeltaIJ[x][y] = mDeltaInit; mWeightChange[x][y] = 0D; } } setCycle(0); // nicht vergessen ! }10. Der Name des Verfahrens ist in der Methode public String getAlgorithmName() festzulegen. Der Name sollte eindeutig sein, d.h. mit keinem anderen Namen eines Lernverfahrens identisch sein. Wird ein Basisalgorithmus durch weitere Terme (Weight Decay, Momentumterm) erweitert, so sollte dies auch im Namen ersichtlich werden (z.B. "Backpropagation + Weight Decay").
// Eindeutiger Algorithmusname public String getAlgorithmName(){ return "RProp"; }11. Die Methode public void applyWeightUpdate(int pOutputting, int pInputting), welche das Gewicht zwischen den Neuronen pOutputting und pInputting aktualisiert, ist zu implementieren. Wichtig ist, dass zu Begin der Methode diese Methode der Superklasse aufgerufen wird (super.applyWeightUpdate(pOutputting, pInputting);).
// Ändern des Kantengewichtes Wij, i=pOutputting, j=pInputting public void applyWeightUpdate(int pOutputting, int pInputting){ super.applyWeightUpdate(pOutputting, pInputting); // nicht vergessen ! // Berechnen der Gewichtsänderung Delta Wij in eine lokale Variable oder // in einen oben deklarierten Speicher, falls er später noch gebraucht wird. double lGradientDirection = getActualGradients()[pOutputting][pInputting] * mGradientWOld[pOutputting][pInputting]; if (lGradientDirection > 0) mDeltaIJ[pOutputting][pInputting] = Math.min( mDeltaIJ[pOutputting][pInputting]*mDeltaPlus, mDeltaMax); else if (lGradientDirection < 0) mDeltaIJ[pOutputting][pInputting] = Math.max( mDeltaIJ[pOutputting][pInputting]*mDeltaMinus, mDeltaMin); if (getActualGradients()[pOutputting][pInputting]>0) mWeightChange[pOutputting][pInputting] = - 1D*mDeltaIJ[pOutputting][pInputting]; else if (getActualGradients()[pOutputting][pInputting]<0) mWeightChange[pOutputting][pInputting] = mDeltaIJ[pOutputting][pInputting]; else mWeightChange[pOutputting][pInputting] = 0D; // Anwenden der Gewichtsänderung getNet().getWeights()[pOutputting][pInputting] += mWeightChange[pOutputting][pInputting]; // Evtl. speichern alter Werte für nächsten Lernzyklus mGradientWOld[pOutputting][pInputting] = getActualGradients()[pOutputting][pInputting]; }12. Zum Schluss muss der Trainingalgorithmus in den Kode der Klasse TrainingAlgorithmsEnumeration des Packetes mscJNeuralNet.trainingAlgortihms.enumerations eingefügt werden, damit grafische Komponenten ihn automatisch finden können.
public class TrainingAlgorithmsEnumeration implements Enumeration { private final INetTrainingAlgorithm [] smAlgorithms = { new BackpropagationMomentum(), new QuickProp(), new RProp(), new RPropWeightDecay(), new SuperSABMomentum() }; [...]Zum Schluss noch der gesamte Kode des Algorithmus:
public class RProp extends AbstractTrainingAlgorithm { private static final ResourceBundle smMSCResourceBundle = ResourceBundle.getBundle("mscJNeuronalNet_Text"); // Werte, die der Algorithmus benötigt (außer den Gewichten und den aktuellen Gradienten) // Initialisierung hier noch nicht möglich, da das Netz noch unbekannt ist private double [][] mGradientWOld; private double [][] mDeltaIJ; private double [][] mWeightChange; // Von aussen konfigurierbare Parameter private static final String smParameterNames [] = { smMSCResourceBundle.getString("DELTA_PLUS"), smMSCResourceBundle.getString("DELTA_MINUS"), smMSCResourceBundle.getString("DELTA_MAX"), smMSCResourceBundle.getString("DELTA_MIN"), smMSCResourceBundle.getString("DELTA_ZERO") }; // Defaultwerte für die Parameter private static final double [] smParameterDefaultValues = {1.2, .5, 50D, .0000001, .1}; private double mDeltaPlus = smParameterDefaultValues[0]; private double mDeltaMinus = smParameterDefaultValues[1]; private double mDeltaMax = smParameterDefaultValues[2]; private double mDeltaMin = smParameterDefaultValues[3]; private double mDeltaInit = smParameterDefaultValues[4]; // Initialisieren des benötigten Speichers. // Erst zu diesem Zeitpunkt ist das Netz gegeben public void init(){ super.init(); // nicht vergessen ! // Grösse der Speicher festlegen mGradientWOld = new double [getNet().getAmountNeurons()][getNet().getAmountNeurons()]; mDeltaIJ = new double [getNet().getAmountNeurons()][getNet().getAmountNeurons()]; mWeightChange = new double [getNet().getAmountNeurons()][getNet().getAmountNeurons()]; // Speicher mit Initialwerten füllen for (int x=0; x0) mDeltaIJ[pOutputting][pInputting] = Math.min( mDeltaIJ[pOutputting][pInputting]*mDeltaPlus, mDeltaMax); else if (lGradientDirection < 0) mDeltaIJ[pOutputting][pInputting] = Math.max( mDeltaIJ[pOutputting][pInputting]*mDeltaMinus, mDeltaMin); if (getActualGradients()[pOutputting][pInputting]>0) mWeightChange[pOutputting][pInputting] = - 1D*mDeltaIJ[pOutputting][pInputting]; else if (getActualGradients()[pOutputting][pInputting]<0) mWeightChange[pOutputting][pInputting] = mDeltaIJ[pOutputting][pInputting]; else mWeightChange[pOutputting][pInputting] = 0D; // Anwenden der Gewichtsänderung getNet().getWeights()[pOutputting][pInputting] += mWeightChange[pOutputting][pInputting]; // Evtl. speichern alter Werte für nächsten Lernzyklus mGradientWOld[pOutputting][pInputting] = getActualGradients()[pOutputting][pInputting]; } // Festlegen der Parameter von aussen // Die Reihenfolge der Parameter legt der Programmierer fest. Sie muss aber mit den Methoden // getLearningParameters, getLearningParameterNames und getDefaultLearningParameters // identisch sein. public void setLearningParameters(double[] pLearningParameters) { if (pLearningParameters.length>0){ if (pLearningParameters[0] >= 0D) mDeltaPlus = pLearningParameters[0]; if (pLearningParameters.length>1){ if (pLearningParameters[1] >= 0D) mDeltaMinus = pLearningParameters[1]; if (pLearningParameters.length>2){ if (pLearningParameters[2] >= 0D) mDeltaMax = pLearningParameters[2]; if (pLearningParameters.length>3){ if (pLearningParameters[3] >= 0D) mDeltaMin = pLearningParameters[3]; if (pLearningParameters.length>4){ if (pLearningParameters[4] >= 0D) mDeltaInit = pLearningParameters[4]; } } } } } getObserverManager().notifyObservers(this); // nicht vergessen ! } // Lesen der Parameter // Reihenfolge wie bei setLearningParameters public double[] getLearningParameters() { double [] lParameters = new double[smParameterNames.length]; lParameters[0] = mDeltaPlus; lParameters[1] = mDeltaMinus; lParameters[2] = mDeltaMax; lParameters[3] = mDeltaMin; lParameters[4] = mDeltaInit; return lParameters; } // Lesen der Bezeichnungen für die Parameter. // Reihenfolge wie bei setLearningParameters public String[] getLearningParameterNames() { return smParameterNames; } // Lesen der Default Parameterwerte. // Reihenfolge wie bei setLearningParameters public double [] getDefaultLearningParameters(){ return smParameterDefaultValues; } // Eindeutiger Algorithmusname public String getAlgorithmName(){ return "RProp"; } // Wie man will. Am besten Name und die aktuellen Prameterwerte. public String toString(){ return "RProp d+: "+mDeltaPlus+" d-: "+mDeltaMinus+" d0: "+mDeltaInit+" dmax: "+mDeltaMax+" dmin: "+mDeltaMin ; } }
![]() mscJNeuralNet
|